package lv.imho.server.services.population;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.google.appengine.api.datastore.Text;

import lv.imho.client.model.DataSourceTransferObject;
import lv.imho.client.model.DataTransferObject;
import lv.imho.client.services.DataService;
import lv.imho.server.model.Data;
import lv.imho.server.services.DataServiceImpl;
import lv.imho.server.services.population.entities.SOResourceResult;
import lv.imho.utils.Utils;

/**
 * Responsible for acquiring processed texts.
 * 
 * @author Igors Gulbinskis
 */
public class DataPopulationDeliveryService extends HttpServlet {
	
	private static final Logger logger = Logger.getLogger(DataPopulationDeliveryService.class.getName());	
	private static final long   serialVersionUID = 1L;
	
	private final String SEMANTIC_ORIENTATION_WS_HOST = "ponijs.lv";
	private final int    SEMANTIC_ORIENTATION_WS_PORT = 8887;
	
	private DataService dataService;
	
	public DataPopulationDeliveryService() {
		super();
		dataService = new DataServiceImpl();
	}
	
	/* manual parsing of semantic orientation service response */
	private List<SOResourceResult> fromXML(String xml) {
		List<SOResourceResult> result = new ArrayList<SOResourceResult>();
		if (xml != null) {
			try {
				DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
				DocumentBuilder builder = factory.newDocumentBuilder();			
				Document document = builder.parse(new ByteArrayInputStream(xml.getBytes("UTF-8")));
				document.getDocumentElement().normalize();
				NodeList list = document.getElementsByTagName("semanticOrientationResourceResult");
												
				Element element;
				if (list.getLength() > 0) {
					SOResourceResult so;
					for (int i = 0; i < list.getLength(); i++) {
						element = (Element) list.item(i);
						
						so = new SOResourceResult();
						so.setDataSourceId(
							Long.parseLong(
								element.getElementsByTagName("dataSourceId").item(0).getTextContent()
							)
						);
						so.setHeading(element.getElementsByTagName("heading").item(0).getTextContent());
						so.setText(element.getElementsByTagName("text").item(0).getTextContent());
						so.setEvaluation(
							Double.parseDouble(
								element.getElementsByTagName("evaluation").item(0).getTextContent()
							)
						);
						so.setTime(
							new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S z").parse(
								element.getElementsByTagName("time").item(0).getTextContent()
							)
						);
						so.setSkipped(false);
						
						Node     phrasesElement;
						Node     orientationsElement;
						NodeList phrases;
						NodeList orientations;
						
						phrasesElement = element.getElementsByTagName("neutralPhrases").item(0);
						phrases = ((Element) phrasesElement).getElementsByTagName("string");
						String neutralPhrases[] = new String[phrases.getLength()];
						if (phrases.getLength() > 0) {							
							for (int j = 0; j < phrases.getLength(); j++) {
								String phrase = phrases.item(j).getTextContent();
								neutralPhrases[j] = phrase;
							}
						}						
						so.setNeutralPhrases(neutralPhrases);
						
						phrasesElement = element.getElementsByTagName("positivePhrases").item(0);
						phrases = ((Element) phrasesElement).getElementsByTagName("string");
						String positivePhrases[] = new String[phrases.getLength()];
						if (phrases.getLength() > 0) {							
							for (int j = 0; j < phrases.getLength(); j++) {
								String phrase = phrases.item(j).getTextContent();
								positivePhrases[j] = phrase;
							}
						}						
						so.setPositivePhrases(positivePhrases);
						
						phrasesElement = element.getElementsByTagName("negativePhrases").item(0);
						phrases = ((Element) phrasesElement).getElementsByTagName("string");
						String negativePhrases[] = new String[phrases.getLength()];
						if (phrases.getLength() > 0) {							
							for (int j = 0; j < phrases.getLength(); j++) {
								String phrase = phrases.item(j).getTextContent();
								negativePhrases[j] = phrase;
							}
						}																		
						so.setNegativePhrases(negativePhrases);						
						
						orientationsElement = element.getElementsByTagName("positiveSemanticOrientations").item(0);
						orientations = ((Element) orientationsElement).getElementsByTagName("double");
						Double postiveSemanticOrientations[] = new Double[orientations.getLength()];
						if (orientations.getLength() > 0) {
							for (int j = 0; j < orientations.getLength(); j++) {
								String sSemanticOrientation = orientations.item(j).getTextContent();
								Double dSemanticOrientation = Double.parseDouble(sSemanticOrientation);
								postiveSemanticOrientations[j] = dSemanticOrientation;
							}
						}
						so.setPositiveSemanticOrientations(postiveSemanticOrientations);
						
						orientationsElement = element.getElementsByTagName("negativeSemanticOrientations").item(0);
						orientations = ((Element) orientationsElement).getElementsByTagName("double");
						Double negativeSemanticOrientations[] = new Double[orientations.getLength()];
						if (orientations.getLength() > 0) {
							for (int j = 0; j < orientations.getLength(); j++) {
								String sSemanticOrientation = orientations.item(j).getTextContent();
								Double dSemanticOrientation = Double.parseDouble(sSemanticOrientation);
								negativeSemanticOrientations[j] = dSemanticOrientation;
							}
						}
						so.setNegativeSemanticOrientations(negativeSemanticOrientations);
						
						result.add(so);
					}
				}				
			}catch(Exception e) {
				logger.warning("exception while parsing xml");
				e.printStackTrace();
			}
		}
		return result;
	}
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		int total = 0;
		String errors = "";		
		
		HttpURLConnection conn = null;		
		try {										
			URL url = new URL("http", SEMANTIC_ORIENTATION_WS_HOST, SEMANTIC_ORIENTATION_WS_PORT, "/getSemanticOrientation/deliver");
			conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod("POST");
			conn.setDoOutput(true);
			conn.connect();
			
			/* write response */
			BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), Charset.forName("UTF-8")));
			
			String xml = "";
			String line;
			while ((line = in.readLine()) != null) {
				xml += line;
			}
			logger.info("xml = " + xml);						
			
			List<SOResourceResult> delivery = new ArrayList<SOResourceResult>();
			/* TODO: wait until GAE is patched */
			// XStream xstream = new XStream();
			// xstream.alias("semanticOrien tationResourceResult", SOResourceResult.class);
			// delivery = (List<SOResourceResult>) xstream.fromXML(xml);
			delivery = fromXML(xml);
			
			/* store results into datastore */
			for (SOResourceResult result : delivery) {
				Data data = new Data();
				data.setText(new Text(result.getText()));
				data.setHeading(result.getHeading());
				data.setEvaluation(result.getEvaluation());
				data.setTime(result.getTime());
				data.setSkipped(false);
				data.setNeutralPhrases(result.getNeutralPhrases());
				data.setPositivePhrases(result.getPositivePhrases());
				data.setPositiveSemanticOrientations(result.getPositiveSemanticOrientations());
				data.setNegativePhrases(result.getNegativePhrases());
				data.setNegativeSemanticOrientations(result.getNegativeSemanticOrientations());
				
				DataSourceTransferObject dataSourceTransferObject = new DataSourceTransferObject();
				dataSourceTransferObject.setId(result.getDataSourceId());
				
				DataTransferObject dataTransferObject = Utils.getDataTransferObject(data, dataSourceTransferObject);				
				if (!dataService.exists(dataTransferObject)) {
					dataService.saveData(dataTransferObject);
					total++;
				}
			}
			
			if (conn.getResponseCode() != 200) {
				throw new Exception("HTTP status code is " + conn.getResponseCode());
			}
		} catch (Exception e) {
			if (conn != null) {
				conn.disconnect();
			}
			errors += "<li>" + e.getMessage() + "</li>";				
			e.printStackTrace();
		}
		
		if ("".equals(errors)) {
			logger.info("data successfully delivered");
		} else {
			logger.info("some errors occured during data delivering");
		}
		resp.getWriter().println(
			"<html>" +
				"<title>inmyhumbleopinion.lv | Data data delivery</title>" +
				"<body>" +
					(!"".equals(errors) ? "<h2>Some errors occured during the data delivery (refer to logs)</h2><ol>" + errors + "</ol>" :
						"<h2>Feeds were successfully delivered!</h2><p>" + total + " new feeds</p>") +
				"</body>" +
			"</html>"				
		);
	}
	
}
